/*
 * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/linkage.h>
#include "hardware.h"

#define DDRC_MSTR		0x0
#define DDRC_STAT		0x4
#define DDRC_MRCTRL0		0x10
#define DDRC_MRCTRL1		0x14
#define DDRC_MRSTAT		0x18
#define DDRC_PWRCTL		0x30
#define DDRC_RFSHCTL3		0x60
#define DDRC_RFSHTMG		0x64
#define DDRC_DBG1		0x304
#define DDRC_SWCTL		0x320
#define DDRC_SWSTAT		0x324
#define DDRC_PSTAT		0x3fc
#define DDRC_PCTRL_0		0x490
#define DDRC_ZQCTL0		0x180
#define DDRC_DFIMISC		0x1b0
#define DDRC_DBGCAM		0x308
#define DDRPHY_LP_CON0		0x18
#define IOMUXC_GPR8		0x20
#define DDRPHY_MDLL_CON0	0xb0
#define DDRPHY_MDLL_CON1	0xb4
#define DDRPHY_OFFSETD_CON0	0x50
#define DDRPHY_OFFSETR_CON0	0x20
#define DDRPHY_OFFSETR_CON1	0x24
#define DDRPHY_OFFSETR_CON2	0x28
#define DDRPHY_OFFSETW_CON0	0x30
#define DDRPHY_OFFSETW_CON1	0x34
#define DDRPHY_OFFSETW_CON2	0x38
#define DDRPHY_CA_WLDSKEW_CON0	0x6c
#define DDRPHY_CA_DSKEW_CON0	0x7c
#define DDRPHY_CA_DSKEW_CON1	0x80
#define DDRPHY_CA_DSKEW_CON2	0x84

#define ANADIG_DIGPROG		0x800

	.align 3

	.macro	switch_to_below_100m

	ldr	r7, =0x2
	str	r7, [r4, #DDRC_DBG1]

	ldr	r6, =0x36000000
1:
	ldr	r7, [r4, #DDRC_DBGCAM]
	and	r7, r7, r6
	cmp	r7, r6
	bne	1b

	ldr	r6, =0x1
2:
	ldr	r7, [r4, #DDRC_MRSTAT]
	and	r7, r7, r6
	cmp	r7, r6
	beq	2b

	ldr	r7, =0x10f0
	str	r7, [r4, #DDRC_MRCTRL0]
	ldr	r7, =0x0
	str	r7, [r4, #DDRC_MRCTRL1]
	ldr	r7, =0x800010f0
	str	r7, [r4, #DDRC_MRCTRL0]

	ldr	r6, =0x1
3:
	ldr	r7, [r4, #DDRC_MRSTAT]
	and	r7, r7, r6
	cmp	r7, r6
	beq	3b

	ldr	r7, =0x20f0
	str	r7, [r4, #DDRC_MRCTRL0]
	ldr	r7, =0x8
	str	r7, [r4, #DDRC_MRCTRL1]
	ldr	r7, =0x800020f0
	str	r7, [r4, #DDRC_MRCTRL0]

	ldr	r6, =0x1
4:
	ldr	r7, [r4, #DDRC_MRSTAT]
	and	r7, r7, r6
	cmp	r7, r6
	beq	4b

	ldr	r7, =0x10f0
	str	r7, [r4, #DDRC_MRCTRL0]
	ldr	r7, =0x1
	str	r7, [r4, #DDRC_MRCTRL1]
	ldr	r7, =0x800010f0
	str	r7, [r4, #DDRC_MRCTRL0]

	ldr	r7, =0x20
	str	r7, [r4, #DDRC_PWRCTL]

	ldr	r6, =0x23
5:
	ldr	r7, [r4, #DDRC_STAT]
	and	r7, r7, r6
	cmp	r7, r6
	bne	5b

	ldr	r7, =0x0
	str	r7, [r4, #DDRC_SWCTL]

	ldr	r7, [r4, #DDRC_MSTR]
	and	r6, r7, #0x400
	ldr	r7, =0x03048001
	/* Set 10th bit in new value to be same as old */
	orr	r7, r7, r6
	str	r7, [r4, #DDRC_MSTR]

	ldr	r7, =0x1
	str	r7, [r4, #DDRC_SWCTL]

	ldr	r6, =0x1
6:
	ldr	r7, [r4, #DDRC_SWSTAT]
	and	r7, r7, r6
	cmp	r7, r6
	bne	6b

	ldr	r7, =0x10010100
	str	r7, [r5, #0x4]

	ldr	r6, =24000000
	cmp	r0, r6
	beq	25f

	ldr	r7, =0x000B000D
	str	r7,[r4, #DDRC_RFSHTMG]
	b	7f

25:
	ldr	r7, =0x00030004
	str	r7,[r4, #DDRC_RFSHTMG]

	/* dram alt sel set to OSC */
	ldr	r7, =0x10000000
	ldr	r8, =0xa080
	str	r7, [r2, r8]
	/* dram root set to from dram alt, div by 1 */
	ldr	r7, =0x11000000
	ldr	r8, =0x9880
	str	r7, [r2, r8]
	b	8f
7:
	/* dram alt sel set to pfd0_392m */
	ldr	r7, =0x15000000
	ldr	r8, =0xa080
	str	r7, [r2, r8]
	/* dram root set to from dram alt, div by 4 */
	ldr	r7, =0x11000003
	ldr	r8, =0x9880
	str	r7, [r2, r8]
8:
	ldr	r7, =0x202ffd0
	str	r7, [r5, #DDRPHY_MDLL_CON0]

	ldr	r7, =0x1000007f
	str	r7, [r5, #DDRPHY_OFFSETD_CON0]

	ldr	r7, =0x7f7f7f7f
	str	r7, [r5, #DDRPHY_OFFSETR_CON0]
	str	r7, [r5, #DDRPHY_OFFSETR_CON1]
	ldr	r7, =0x7f
	str	r7, [r5, #DDRPHY_OFFSETR_CON2]

	ldr	r7, =0x7f7f7f7f
	str	r7, [r5, #DDRPHY_OFFSETW_CON0]
	str	r7, [r5, #DDRPHY_OFFSETW_CON1]
	ldr	r7, =0x7f
	str	r7, [r5, #DDRPHY_OFFSETW_CON2]

	ldr	r7, [r9, #ANADIG_DIGPROG]
	and	r7, r7, #0x11
	cmp	r7, #0x11
	bne	20f

	ldr	r7, =0x0
	str	r7, [r5, #DDRPHY_CA_WLDSKEW_CON0]
	ldr	r7, =0x60606060
	str	r7, [r5, #DDRPHY_CA_DSKEW_CON0]
	str	r7, [r5, #DDRPHY_CA_DSKEW_CON1]
	ldr	r7, =0x00006060
	str	r7, [r5, #DDRPHY_CA_DSKEW_CON2]
	b	21f
20:
	ldr	r7, =0x0
	str	r7, [r5, #DDRPHY_CA_DSKEW_CON0]
	str	r7, [r5, #DDRPHY_CA_DSKEW_CON1]
	str	r7, [r5, #DDRPHY_CA_DSKEW_CON2]
21:
	ldr	r7, =0x1100007f
	str	r7, [r5, #DDRPHY_OFFSETD_CON0]
	ldr	r7, =0x1000007f
	str	r7, [r5, #DDRPHY_OFFSETD_CON0]

	ldr	r7, =0x0
	str	r7, [r4, #DDRC_PWRCTL]

	ldr	r6, =0x1
9:
	ldr	r7, [r4, #DDRC_MRSTAT]
	and	r7, r7, r6
	cmp	r7, r6
	beq	9b

	ldr	r7, =0xf0
	str	r7, [r4, #DDRC_MRCTRL0]
	ldr	r7, =0x820
	str	r7, [r4, #DDRC_MRCTRL1]
	ldr	r7, =0x800000f0
	str	r7, [r4, #DDRC_MRCTRL0]

	ldr	r6, =0x1
10:
	ldr	r7, [r4, #DDRC_MRSTAT]
	and	r7, r7, r6
	cmp	r7, r6
	beq	10b

	ldr	r7, =0x800020
	str	r7, [r4, #DDRC_ZQCTL0]

	ldr	r7, =0x0
	str	r7, [r4, #DDRC_DBG1]

	/* enable auto self-refresh */
	ldr	r7, [r4, #DDRC_PWRCTL]
	orr	r7, r7, #(1 << 0)
	str	r7, [r4, #DDRC_PWRCTL]

	.endm

	.macro	switch_to_533m

	ldr	r7, =0x2
	str	r7, [r4, #DDRC_DBG1]

	ldr	r7, =0x78
	str	r7, [r3, #IOMUXC_GPR8]
	orr	r7, r7, #0x100
	str	r7, [r3, #IOMUXC_GPR8]

	ldr	r6, =0x30000000
11:
	ldr	r7, [r4, #DDRC_DBGCAM]
	and	r7, r7, r6
	cmp	r7, r6
	bne	11b

	ldr	r6, =0x1
12:
	ldr	r7, [r4, #DDRC_MRSTAT]
	and	r7, r7, r6
	cmp	r7, r6
	beq	12b

	ldr	r7, =0x10f0
	str	r7, [r4, #DDRC_MRCTRL0]
	ldr	r7, =0x1
	str	r7, [r4, #DDRC_MRCTRL1]
	ldr	r7, =0x800010f0
	str	r7, [r4, #DDRC_MRCTRL0]

	ldr	r7, =0x20
	str	r7, [r4, #DDRC_PWRCTL]

	ldr	r6, =0x23
13:
	ldr	r7, [r4, #DDRC_STAT]
	and	r7, r7, r6
	cmp	r7, r6
	bne	13b

	ldr	r7, [r4, #DDRC_MSTR]
	and	r6, r7, #0x400
	ldr	r7, =0x03040001
	/* Set 10th bit in new value to be same as old */
	orr	r7, r7, r6
	str	r7, [r4, #DDRC_MSTR]

	ldr	r7, =0x40800020
	str	r7, [r4, #DDRC_ZQCTL0]


	ldr	r7, =0x10210100
	str	r7, [r5, #0x4]

	ldr	r7, =0x00040046
	str	r7, [r4, #DDRC_RFSHTMG]

	/* dram root set to from dram main, div by 2 */
	ldr	r7, =0x10000001
	ldr	r8, =0x9880
	str	r7, [r2, r8]

	ldr	r7, =0x1010007e
	str	r7, [r5, #DDRPHY_MDLL_CON0]

	ldr	r7, =0x10000008
	str	r7, [r5, #DDRPHY_OFFSETD_CON0]

	ldr	r7, =0x08080808
	str	r7, [r5, #DDRPHY_OFFSETR_CON0]
	str	r7, [r5, #DDRPHY_OFFSETR_CON1]
	ldr	r7, =0x8
	str	r7, [r5, #DDRPHY_OFFSETR_CON2]

	ldr	r7, =0x08080808
	str	r7, [r5, #DDRPHY_OFFSETW_CON0]
	str	r7, [r5, #DDRPHY_OFFSETW_CON1]
	ldr	r7, =0x8
	str	r7, [r5, #DDRPHY_OFFSETW_CON2]

	ldr	r7, [r9, #ANADIG_DIGPROG]
	and	r7, r7, #0x11
	cmp	r7, #0x11
	bne	22f

	ldr	r7, =0x40404040
	str	r7, [r5, #DDRPHY_CA_WLDSKEW_CON0]
	ldr	r7, =0x18181818
	str	r7, [r5, #DDRPHY_CA_DSKEW_CON0]
	str	r7, [r5, #DDRPHY_CA_DSKEW_CON1]
	ldr	r7, =0x40401818
	str	r7, [r5, #DDRPHY_CA_DSKEW_CON2]
	b	23f
22:
	ldr	r7, =0x0
	str	r7, [r5, #DDRPHY_CA_DSKEW_CON0]
	str	r7, [r5, #DDRPHY_CA_DSKEW_CON1]
	str	r7, [r5, #DDRPHY_CA_DSKEW_CON2]
23:
	ldr	r7, =0x11000008
	str	r7, [r5, #DDRPHY_OFFSETD_CON0]
	ldr	r7, =0x10000008
	str	r7, [r5, #DDRPHY_OFFSETD_CON0]

	ldr	r6, =0x4
14:
	ldr	r7, [r5, #DDRPHY_MDLL_CON1]
	and	r7, r7, r6
	cmp	r7, r6
	bne	14b

	ldr	r7, =0x1
	str	r7, [r4, #DDRC_RFSHCTL3]
	ldr	r7, =0x3
	str	r7, [r4, #DDRC_RFSHCTL3]

	ldr	r7, =0x0
	str	r7, [r4, #DDRC_PWRCTL]

	ldr	r6, =0x1
15:
	ldr	r7, [r4, #DDRC_MRSTAT]
	and	r7, r7, r6
	cmp	r7, r6
	beq	15b

	ldr	r7, =0x10f0
	str	r7, [r4, #DDRC_MRCTRL0]
	ldr	r7, =0x0
	str	r7, [r4, #DDRC_MRCTRL1]
	ldr	r7, =0x800010f0
	str	r7, [r4, #DDRC_MRCTRL0]

	ldr	r6, =0x1
16:
	ldr	r7, [r4, #DDRC_MRSTAT]
	and	r7, r7, r6
	cmp	r7, r6
	beq	16b

	ldr	r7, =0xf0
	str	r7, [r4, #DDRC_MRCTRL0]
	ldr	r7, =0x930
	str	r7, [r4, #DDRC_MRCTRL1]
	ldr	r7, =0x800000f0
	str	r7, [r4, #DDRC_MRCTRL0]

	ldr	r7, =0x0
	str	r7, [r4, #DDRC_RFSHCTL3]
	ldr	r7, =0x2
	str	r7, [r4, #DDRC_RFSHCTL3]

	ldr	r6, =0x1
17:
	ldr	r7, [r4, #DDRC_MRSTAT]
	and	r7, r7, r6
	cmp	r7, r6
	beq	17b

	ldr	r7, =0xf0
	str	r7, [r4, #DDRC_MRCTRL0]
	ldr	r7, =0x930
	str	r7, [r4, #DDRC_MRCTRL1]
	ldr	r7, =0x800000f0
	str	r7, [r4, #DDRC_MRCTRL0]

	ldr	r6, =0x1
18:
	ldr	r7, [r4, #DDRC_MRSTAT]
	and	r7, r7, r6
	cmp	r7, r6
	beq	18b

	ldr	r7, =0x20f0
	str	r7, [r4, #DDRC_MRCTRL0]
	ldr	r7, =0x408
	str	r7, [r4, #DDRC_MRCTRL1]
	ldr	r7, =0x800020f0
	str	r7, [r4, #DDRC_MRCTRL0]

	ldr	r6, =0x1
19:
	ldr	r7, [r4, #DDRC_MRSTAT]
	and	r7, r7, r6
	cmp	r7, r6
	beq	19b

	ldr	r7, =0x10f0
	str	r7, [r4, #DDRC_MRCTRL0]
	ldr	r7, =0x4
	str	r7, [r4, #DDRC_MRCTRL1]
	ldr	r7, =0x800010f0
	str	r7, [r4, #DDRC_MRCTRL0]

	ldr	r7, =0x0
	str	r7, [r4, #DDRC_DBG1]

	/* enable auto self-refresh */
	ldr	r7, [r4, #DDRC_PWRCTL]
	orr	r7, r7, #(1 << 0)
	str	r7, [r4, #DDRC_PWRCTL]

	.endm

ENTRY(imx7d_ddr3_freq_change)
	push	{r2 - r9}

	/*
	 * To ensure no page table walks occur in DDR, we
	 * have a another page table stored in IRAM that only
	 * contains entries pointing to IRAM, AIPS1 and AIPS2.
	 * We need to set the TTBR1 to the new IRAM TLB.
	 * Do the following steps:
	 * 1. Flush the Branch Target Address Cache (BTAC)
	 * 2. Set TTBR1 to point to IRAM page table.
	 * 3. Disable page table walks in TTBR0 (PD0 = 1)
	 * 4. Set TTBR0.N=1, implying 0-2G is translated by TTBR0
	 *     and 2-4G is translated by TTBR1.
	 */
	ldr	r6, =0x0
	mcr	p15, 0, r6, c8, c3, 0

	ldr	r6, =iram_tlb_phys_addr
	ldr	r7, [r6]

	/* Disable Branch Prediction, Z bit in SCTLR. */
	mrc	p15, 0, r6, c1, c0, 0
	bic	r6, r6, #0x800
	mcr	p15, 0, r6, c1, c0, 0

	/* Flush the Branch Target Address Cache (BTAC) */
	ldr	r6, =0x0
	mcr	p15, 0, r6, c7, c1, 6

	dsb
	isb
	/* Store the IRAM table in TTBR1 */
	mcr	p15, 0, r7, c2, c0, 1

	/* Read TTBCR and set PD0=1, N = 1 */
	mrc	p15, 0, r6, c2, c0, 2
	orr	r6, r6, #0x11
	mcr	p15, 0, r6, c2, c0, 2

	dsb
	isb

	/* flush the TLB */
	ldr	r6, =0x0
	mcr	p15, 0, r6, c8, c3, 0

	dsb
	isb

	ldr	r2, =IMX_IO_P2V(MX7D_CCM_BASE_ADDR)
	ldr	r3, =IMX_IO_P2V(MX7D_IOMUXC_GPR_BASE_ADDR)
	ldr	r4, =IMX_IO_P2V(MX7D_DDRC_BASE_ADDR)
	ldr	r5, =IMX_IO_P2V(MX7D_DDRC_PHY_BASE_ADDR)
	ldr	r9, =IMX_IO_P2V(MX7D_ANATOP_BASE_ADDR)

	ldr	r6, =100000000
	cmp	r0, r6
	bgt	set_to_533m

set_to_below_100m:
	switch_to_below_100m
	b	done

set_to_533m:
	switch_to_533m
	b	done

done:
	/* Enable L1 data cache. */
	mrc	p15, 0, r6, c1, c0, 0
	orr	r6, r6, #0x4
	mcr	p15, 0, r6, c1, c0, 0

	/* Restore the TTBCR */
	dsb
	isb

	/* Read TTBCR and set PD0=0, N = 0 */
	mrc	p15, 0, r6, c2, c0, 2
	bic	r6, r6, #0x11
	mcr	p15, 0, r6, c2, c0, 2
	dsb
	isb

	/* flush the TLB */
	ldr	r6, =0x0
	mcr	p15, 0, r6, c8, c3, 0

	dsb
	isb

	/* Enable Branch Prediction, Z bit in SCTLR. */
	mrc	p15, 0, r6, c1, c0, 0
	orr	r6, r6, #0x800
	mcr	p15, 0, r6, c1, c0, 0

	/* Flush the Branch Target Address Cache (BTAC) */
	ldr	r6, =0x0
	mcr	p15, 0, r6, c7, c1, 6

	dsb
	isb

	nop
	nop
	nop
	nop
	nop

	nop
	nop
	nop
	nop
	nop

	nop
	nop
	nop
	nop
	nop

	nop
	nop
	nop
	nop
	nop

	nop
	nop
	nop
	nop
	nop

	/* Restore registers */
	pop	{r2 - r9}
	mov	pc, lr
	.ltorg
ENDPROC(imx7d_ddr3_freq_change)
